# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# This file tells the HP 33120A waveform generater to output signals of the
# given shape, frequency, and amplitude. The serial communication uses standard
# SCPI over RS232 8N2.
#
# HP 33120A Manual:
# http://sdphca.ucsd.edu/Lab_Equip_Manuals/Agilent_33120a_Users.pdf
#
# Usage: python generate_noise.py [SQUARE/SINE/OFF] [frequency (Hz)]
#           [amplitude (Vpp)]
# Ex: python generate_noise.py SQUARE 3000 20
#
# Using Python 2.7
# ----------------------------------------------
# Mindy Huang (mindyh@google.com)

import glob
import serial
import sys
import time

# Constants
RESPONSE_DELAY_S = 0.5
HIGH_Z_THRESHOLD = 10


# Send and potentially receive a message from the fn generator.
def send_message(message, ser):
    # The newline at the end is necessary to terminate the message.
    ser.write(message + '\n')

    # The device will only respond if prompted with a '?'.
    if '?' in message:
        return ser.readline()
    else:
        # Give the function generator some time to react
        time.sleep(RESPONSE_DELAY_S)


# Check that the port we're talking is the function generator.
def find_function_generator():
    list_of_USB_ports = glob.glob('/dev/ttyUSB*')

    for port_name in list_of_USB_ports:
        ser = serial.Serial(
            port=port_name,
            baudrate=9600,
            stopbits=serial.STOPBITS_TWO,
            bytesize=serial.EIGHTBITS,
            parity=serial.PARITY_NONE,
            timeout=1
        )
        # Query the port to see if it's the generator.
        # The query asks for the make and model of the generator.
        # The correct response should contain the company name, Hewlett-Packard.
        if 'HEWLETT-PACKARD' in send_message('*IDN?', ser):
            return ser

    return None


def generate_function(form, freq, ampl, ser):
    # puts the generator in remote mode
    send_message('SYST:REM', ser)

    if(ampl > HIGH_Z_THRESHOLD):
        # This puts the generator into high impedance (high Z) mode
        # so it can reach up to 20Vpp.
        send_message('OUTP:LOAD INF', ser)
    else:
        # Keeps the generator in low impedance mode (default).
        send_message('OUTP:LOAD 50', ser)

    send_message('APPL:%s %E, %d' % (form, freq, ampl), ser)


def error_message():
    print("Usage: python generate_noise.py [SQUARE/SINE/OFF]" +
          " [frequency (Hz)] [amplitude (Vpp)]")
    print("Ex: python generate_noise.py SQUARE 3000 20")


def main():
    # Open a connection to the function generator
    ser = find_function_generator()
    if not ser:
        print 'Error: cannot find the function generator. Is it connected?'
        return 1  # Can't find the generator, exit the script

    ser.open()

    if(len(sys.argv) > 1):
        if (sys.argv[1] == 'OFF'):  # Turns the function generator off.
            send_message('APPL:DC 1, 1, 0', ser)

        elif (len(sys.argv) == 2):  # The script was given a custom command.
            print send_message(sys.argv[1], ser)

        elif(len(sys.argv) == 4):  # Generate a waveform.
            freq = int(sys.argv[2])
            ampl = int(sys.argv[3])
            form = 'SQU' if (sys.argv[1] == 'SQUARE') else 'SIN'

            generate_function(form, freq, ampl, ser)
    else:
        error_message()

    ser.close()
    return 0


if __name__ == '__main__':
    sys.exit(main())
